//  Copyright (c) 2009 Ray Liang (http://www.dotnetage.com)
//  Dual licensed under the MIT and GPL licenses:
//  http://www.opensource.org/licenses/mit-license.php
//  http://www.gnu.org/licenses/gpl.html

using System;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.IO;
using DNA.Mvc.Services;
using DNA.Mvc.Exporting;

namespace DNA.Mvc.Controllers
{
    //[Log]
    //[MasterLayout]
    [HandleError(View = "Error")]
    public class DynamicUIController : Controller
    {
        private WebSiteContext _context;
        public DynamicUIController(WebSiteContext context) { _context = context; }

        /// <summary>
        /// Create the Dynamic page
        /// </summary>
        /// <param name="page">The page object create from controller</param>
        /// <returns></returns>
        [SecurityAction("Pages", "Create page", "Allows user to create new web page",
     TitleResName = "SA_CreateNewPage",
     DescResName = "SA_CreateNewPageDesc",
     PermssionSetResName = "SA_PAGES")]
        [Authorize, HttpPost]
        public ActionResult Create([Bind(Prefix = "WebPage", Exclude = "ID,Path")] WebPage page, string returnUrl)
        {
            ModelState.Remove("WebPage.Path");
            if (ModelState.IsValid)
            {
                if (!page.AllowAnonymous)
                {
                    var roles = GetPostRoles();
                    if (roles.Length > 0)
                    {
                        foreach (var roleName in roles)
                        {
                            var role = new WebPageRole()
                            {
                                WebPage = page,
                                RoleName = roleName
                            };
                            _context.DataContext.WebPageRoles.Create(role);
                        }
                    }
                    else
                    {
                        page.AllowAnonymous = true;
                    }
                }

                page.Path = GeneratePagePermalink(_context.Web.Name, page);
                page.Web = _context.Web;
                _context.DataContext.WebPages.Create(page);
                _context.DataContext.SaveChanges();

                //For testing
                var url = page.Path + "?design=true";
                if (Url != null)
                {
                    if (!string.IsNullOrEmpty(returnUrl) && Url.IsLocalUrl(returnUrl))
                        url = returnUrl;
                    else
                        url = Url.Content(url);
                }
                return Redirect(url);
            }
            else
                return View(page);
        }

        private string[] GetPostRoles()
        {
            var strRoles = HttpContext.Request.Form["AccessableRoles"];
            if (!string.IsNullOrEmpty(strRoles))
                return strRoles.Split(',');
            else
                return new string[0];
        }

        private string GeneratePagePermalink(string webName, WebPage page)
        {
            string path = TextUtility.Slug(page.Title);
            if ((string.IsNullOrEmpty(webName)) || webName.Equals("home", StringComparison.OrdinalIgnoreCase))
                path = "~/sites/home/" + path;
            else
                path = "~/sites/" + webName.ToLower() + "/" + path;

            if (!_context.DataContext.WebPages.IsExists(path.ToLower() + ".html"))
                path += ".html";
            else
                path += "-" + DateTime.Now.ToString("yyMMddHHmm") + ".html";
            return path;
        }

        /// <summary>
        /// Return the create page view
        /// </summary>
        /// <returns></returns>
        [SecurityAction("Pages", "Create page", "Allows user to create new web page",
             TitleResName = "SA_CreateNewPage",
             DescResName = "SA_CreateNewPageDesc",
             PermssionSetResName = "SA_PAGES"
         )]
        public ActionResult Create()
        {
            return View();
        }

        /// <summary>
        /// Return the edit page view
        /// </summary>
        /// <param name="id">Specified the page id to edit.</param>
        /// <param name="forms"></param>
        /// <returns>The edit page view</returns>
        [SecurityAction("Pages", "Edit page", "Allows user to edit the page data.",
    TitleResName = "SA_EditPage", DescResName = "SA_EditPageDesc", PermssionSetResName = "SA_PAGES")]
        [HttpPost]
        public ActionResult Edit(int id, FormCollection forms)
        {
            //    ModelState.Remove("ParentID");
            if (id == 0) throw new ArgumentNullException("id");
            var page = _context.DataContext.WebPages.Find(id);

            if (page == null)
                return HttpNotFound();

            if (forms["Slug"] != null)
            {
                if (!page.IsShared && string.IsNullOrEmpty(page.ViewData))
                {
                    if (string.IsNullOrEmpty(forms["Slug"]))
                    {
                        ModelState.AddModelError("", "The file name could not be empty");
                        return View(page);
                    }

                    var slug = TextUtility.Slug(forms["Slug"].ToString());

                    if (!slug.EndsWith(".html", StringComparison.OrdinalIgnoreCase))
                        slug += ".html";

                    var _orgSlug = System.IO.Path.GetFileName(page.Path);

                    if (slug != _orgSlug)
                    {
                        string path = page.Path.Replace(_orgSlug, slug);
                        if (_context.DataContext.WebPages.IsExists(path))
                        {
                            ModelState.AddModelError("", "The target page file name is exists.");
                            return View(page);
                        }
                         
                        page.Path = path;
                    }
                }
            }

            if (ModelState.IsValid)
            {
                if (TryUpdateModel<WebPage>(page,"WebPage", new string[] { 
                    "Title", "Description","Keywords","LinkUrl", 
                    "ViewName", "Target", "ParentID", "ShowInMenu", 
                    "AllowAnonymous","ImageUrl","IconUrl" }))
                {
                    page.Roles.Clear();
                    var strRoles = forms["AccessableRoles"];
                    var roles = !string.IsNullOrEmpty(strRoles) ? strRoles.Split(',') : null;

                    if ((roles != null) && (roles.Length > 0))
                        _context.DataContext.WebPages.AddRoles(page, roles);
                    _context.DataContext.SaveChanges();
                }

                if ((Request.QueryString != null) && (!string.IsNullOrEmpty(Request.QueryString["returnUrl"])))
                    return Redirect(Request.QueryString["returnUrl"]);

                if (Url != null)
                    return Redirect(Url.Content(page.Path));
                else
                    return View(page);
            }
            else
                return View(page);
        }

        /// <summary>
        /// Save the page changes
        /// </summary>
        /// <param name="id"></param>
        /// <returns></returns>
        [SecurityAction("Pages", "Edit page", "Allows user to edit the page data.",
            TitleResName = "SA_EditPage",
            DescResName = "SA_EditPageDesc",
            PermssionSetResName = "SA_PAGES"
        )]
        public ActionResult Edit(int id)
        {
            var page = _context.DataContext.WebPages.Find(id);

            if (!page.Owner.Equals(User.Identity.Name))
            {
                if (!_context.IsAuthorized<DynamicUIController>("Edit"))
                    return new HttpNotFoundResult();
            }

            if (page == null)
                return HttpNotFound();
            return View(page);
        }

        /// <summary>
        /// Delete the page by specified page id.
        /// </summary>
        ///<param name="id">The page id.</param>
        [HttpPost]
        [SecurityAction("Pages", "Delete page", "Allows the users can delete the page.",
            TitleResName = "SA_DeletePage",
            DescResName = "SA_DeletePageDesc",
            PermssionSetResName = "SA_PAGES"
        )]
        public void Delete(int id)
        {
            _context.DataContext.WebPages.Delete(id);
            _context.DataContext.SaveChanges();
        }

        /// <summary>
        /// Display the dyanmic page by specified page name.
        /// </summary>
        /// <remarks>
        ///  If the specified page name not found this action will redirect to the NotFound page.
        /// </remarks>
        /// <param name="layout">The dynamic page name</param>
        /// <returns></returns>
        [SiteMapAction]
        //[OutputCache(Duration=60,VaryByParam="*")]
        public ActionResult Index(string layout)
        {
            var page = _context.Page;

            if (page == null)
            {
                if ((HttpContext.Request.IsAuthenticated) && (_context.Web == null) && (HttpContext.Request.Url.LocalPath.EndsWith("index.html", StringComparison.OrdinalIgnoreCase)))
                {
                    if (_context.EnablePersonalWeb)
                    {
                        var website = HttpContext.Request.RequestContext.RouteData.Values["website"] as string;
                        if ((!string.IsNullOrEmpty(website) && (website.Equals(User.Identity.Name, StringComparison.OrdinalIgnoreCase))))
                            return RedirectToAction("SetupMysite", "Solution", new { Area="" , website=""});
                    }
                    else
                        return Redirect(User.GetPermalinkUrl().ToString());
                }
                return new HttpNotFoundResult();
                //throw new HttpException(404, "Page not found.");
            }

            if (!HttpContext.Request.IsAuthenticated)
            {
                if (!page.AllowAnonymous)
                    return new HttpUnauthorizedResult();
            }

            if (!string.IsNullOrEmpty(page.LinkUrl))
                return Redirect(Url.Content(page.LinkUrl));

            ViewData["Layout"] = page.ViewName;
            return View();
        }


        [SiteControlPanel(ResKey = "RES_PAGEMAN", Order = 1, GroupResKey = "RES_CONTENT",Group="Content")]
        [MyControlPanel(ResKey = "RES_PAGEMAN", Order = 1, GroupResKey = "RES_CONTENT",Group="Content" ,ShowInPersonalSiteOnly = true)]
        [SecurityAction("Pages", "Page manager", "Allows the users can access the page manager",
            TitleResName = "SA_PageMan",
            DescResName = "SA_PageManDesc",
            PermssionSetResName = "SA_PAGES"
        )]
        public ActionResult PageManager()
        {
            return PartialView();
        }

        [Authorize]
        public ActionResult SubPages(int parentID)
        {
            //throw new NotImplementedException();
            //ViewData.Model = this.CurrentWeb().Children(parentID).OrderBy(p => p.Pos);// Service.GetChildPages(parentID);
            //ViewData["Page"] = this.CurrentWeb().GetPage(parentID);

            ViewData.Model = _context.DataContext.WebPages.GetChildren(_context.Web.Id, parentID).OrderBy(p => p.Pos);
            ViewData["Page"] = _context.DataContext.WebPages.Find(parentID);
            return PartialView();
        }

        /// <summary>
        /// Move the page position
        /// </summary>
        /// <param name="id">Specified the page id</param>
        /// <param name="parentID">Specified the new parent id of the page</param>
        /// <param name="pos">Specified the page new pos</param>
        [HttpPost]
        [SecurityAction("Pages", "Change position", "Allows users can move the page to a new position",
            TitleResName = "SA_MoveChange",
            DescResName = "SA_MoveChangeDesc",
            PermssionSetResName = "SA_PAGES"
        )]
        public void Move(int id, int parentID, int pos)
        {
            using (_context.DataContext)
            {
                if (parentID != id)
                {
                    _context.DataContext.WebPages.Move(parentID, id, pos);
                    var updateCount = _context.DataContext.SaveChanges();
                }
            }
        }

        /// <summary>
        /// Set the specified page to default page
        /// </summary>
        /// <param name="id">Specified the page id</param>
        [Authorize, HttpPost]
        //[SecurityAction("Pages", "Set default page", "Allows users can set the page as default page of the web.")]
        public void SetDefault(int id)
        {
            using (_context.DataContext)
            {
                _context.DataContext.WebPages.SetToDefault(_context.Web.Id, id);
                _context.DataContext.SaveChanges();
            }
        }

        [Authorize, HttpPost]
        public void SetVisible(int id, bool value)
        {
            using (_context.DataContext)
            {
                var p = _context.DataContext.WebPages.Find(id);
                p.ShowInMenu = value;
                _context.DataContext.Update(p);
                _context.DataContext.SaveChanges();
            }
        }

        /// <summary>
        /// Import the page json data file and insert page instance into system.
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [SecurityAction("Pages", "Import page data", "Allows users can upload the page data file and create a new page.",
            TitleResName = "SA_ImportPageData",
            DescResName = "SA_ImportPageDataDesc",
            PermssionSetResName = "SA_PAGES"
        )]
        public void Import(int id)
        {
            if (id == 0) throw new ArgumentOutOfRangeException("");

            if (Request.Files.Count == 0) throw new HttpException("Upload file not found");

            if (Request.Files[0].ContentLength == 0) throw new HttpException("Upload file have not content.");

            var postedFile = Request.Files[0];
            WebPageTemplate template = TemplateHelper.LoadWebPageTemplate(postedFile.InputStream);

            using (_context.DataContext)
            {
                var page = _context.DataContext.WebPages.Find(id);

                if (page == null)
                    throw new HttpException(404, "Web Page not found");

                page.From(template);
                page.Widgets.Clear();
                _context.DataContext.SaveChanges();

                foreach (var widgetTmpl in template.Widgets)
                    _context.DataContext.Widgets.AddWidget(Guid.NewGuid(), widgetTmpl, page.Path);
                _context.DataContext.SaveChanges();
            }
        }

        /// <summary>
        /// Export specified page instance to the json data file.
        /// </summary>
        /// <param name="id">Specified the page id</param>
        /// <returns>Json data file</returns>
        [SecurityAction("Pages", "Export page data", "Allows users can export and download the exists page data.",
            TitleResName = "SA_ExportPageData",
            DescResName = "SA_ExportPageDataDesc",
            PermssionSetResName = "SA_PAGES"
        )]
        public ActionResult Export(int id)
        {
            if (id == 0) throw new ArgumentOutOfRangeException("id");

            var page = _context.DataContext.WebPages.Find(id);

            if (page == null)
                return HttpNotFound();

            var stream = new MemoryStream();
            var writer = new StreamWriter(stream);
            try
            {
                writer.Write(page.ToXml());
            }
            catch (Exception e)
            {
                return Content("Could not export the page data.Mybe the xml document no correct.");
                //string.Format("<h1>{0}</h1><p>{1}</p>", e.Message, e.StackTrace));
            }
            writer.Flush();
            stream.Position = 0;

            return File(stream, "text/xml", TextUtility.Slug(page.Title) + "-" + DateTime.Now.ToString("yy-MM-dd") + ".xml");
        }

        public ActionResult SEOSuggestions()
        {
            var web = _context.Web;
            var allPages = _context.DataContext.WebPages.Filter(p => p.WebID == web.Id).ToList();

            var missingTitlePages = allPages.Where(p => string.IsNullOrEmpty(p.Title));
            var missingDescPages = allPages.Where(p => string.IsNullOrEmpty(p.Description));
            var missingKeywordsPages = allPages.Where(p => string.IsNullOrEmpty(p.Keywords));

            var shortDescPages = allPages.Where(p => !string.IsNullOrEmpty(p.Description) && p.Description.Length < 25);
            var longDescPages = allPages.Where(p => !string.IsNullOrEmpty(p.Description) && p.Description.Length > 150);

            var shortTitlePages = allPages.Where(p => !string.IsNullOrEmpty(p.Title) && p.Title.Length < 5);
            var longTitlePages = allPages.Where(p => !string.IsNullOrEmpty(p.Title) && p.Title.Length > 65);

            ViewBag.MissingTitle = missingTitlePages.ToList();
            ViewBag.MissingDesc = missingDescPages.ToList();
            ViewBag.MissingKeywords = missingKeywordsPages.ToList();

            ViewBag.ShortDescPages = shortDescPages;
            ViewBag.ShortTitlePages = shortTitlePages;
            ViewBag.LongDescPages = longDescPages;
            ViewBag.LongTitlePages = longTitlePages;

            return View();
        }

        [Authorize]
        public ActionResult PageDetail(int id)
        {
            return PartialView(_context.DataContext.WebPages.Find(id));
        }

        //// [OutputCache(Duration = int.MaxValue, VaryByParam = "none")]
        //public ActionResult SiteMapIndex()
        //{
        //    //using (var db = DBMan.Instance())
        //    //{
        //    //    //Prepare folders
        //    //    string sitemapPath = Server.MapPath("~/shared/public/sitemaps/");

        //    //    if (!Directory.Exists(sitemapPath))
        //    //        Directory.CreateDirectory(sitemapPath);

        //    //    var _sitemapIndex = new SitemapIndex();
        //    //    _sitemapIndex.SiteMapFiles = new List<Sitemap>();
        //    //    var _pageSitemap = new Sitemap();

        //    //    var rootPath = Request.Url.Scheme + "://" + Request.Url.Authority;
        //    //    _pageSitemap.Location = rootPath + "/webshared/home/sitemaps/pages.xml";
        //    //    var _recreatePageSitemap = false;

        //    //    //#1 generate sitemap for webpages
        //    //    if (System.IO.File.Exists(sitemapPath + "pages.xml"))
        //    //    {
        //    //        var fileInfo = new FileInfo(sitemapPath + "pages.xml");
        //    //        if ((DateTime.Now - fileInfo.CreationTime).Days == 0)
        //    //            _pageSitemap.LastModified = fileInfo.CreationTime.ToString("yyyy-MM-dd");
        //    //        else
        //    //            _recreatePageSitemap = true;
        //    //    }
        //    //    else
        //    //        _recreatePageSitemap = true;

        //    //    if (_recreatePageSitemap)
        //    //    {
        //    //        var pages = from p in db.WebPages
        //    //                    where p.AllowAnonymous
        //    //                    select p;
        //    //        int _curWebID = WebSite.Open().Id;
        //    //        var _pageUrlset = new SitemapUrlset() { Urls = new List<SitemapUrl>() };
        //    //        foreach (var page in pages)
        //    //        {
        //    //            if (page.WebID != _curWebID) continue;
        //    //            _pageUrlset.Urls.Add(new SitemapUrl()
        //    //            {
        //    //                Url = page.PermalinkUrl.ToString(),
        //    //                LastModified = page.LastModified.ToString("yyyy-MM-dd"),
        //    //                Priority = 0.5M,
        //    //                ChangeFrequently = SitemapChangeFrequently.Weekly
        //    //            });
        //    //        }
        //    //        _pageSitemap.LastModified = DateTime.Now.ToString("yyyy-MM-dd");
        //    //        DNA.Utility.XmlSerializerUtility.SerializeToXmlFile(sitemapPath + "pages.xml", typeof(SitemapUrlset), _pageUrlset);
        //    //    }
        //    //    _sitemapIndex.SiteMapFiles.Add(_pageSitemap);

        //    //    //#2 generate sitemap for archives
        //    //    var archives = WebSite.Open().GetArchives();
        //    //    foreach (var archive in archives)
        //    //    {
        //    //        var archiveFileName = "archive-" + archive.Year.ToString() + "-" + archive.Month.ToString() + ".xml";
        //    //        var archiveFile = sitemapPath + archiveFileName;
        //    //        var _archiveSitemap = new Sitemap();
        //    //        _archiveSitemap.LastModified = new FileInfo(archiveFile).CreationTime.ToString("yyyy-MM-dd");

        //    //        if ((archive.Year == DateTime.Now.Year) && (archive.Month == DateTime.Now.Month) && System.IO.File.Exists(archiveFile))
        //    //            System.IO.File.Delete(archiveFile);

        //    //        if (!System.IO.File.Exists(archiveFile))
        //    //        {
        //    //            _archiveSitemap.LastModified = DateTime.Now.ToString("yyyy-MM-dd");
        //    //            var articles = archive.GetArticles();
        //    //            var _arcivheUrlset = new SitemapUrlset() { Urls = new List<SitemapUrl>() };
        //    //            foreach (var article in articles)
        //    //            {
        //    //                if ((article.IsPrivate) || (!article.IsAppoved) || (!article.IsPublished)) continue;
        //    //                _arcivheUrlset.Urls.Add(new SitemapUrl()
        //    //                {
        //    //                    Url = article.GetPermaLinkUrl(),
        //    //                    LastModified = article.LastModified.ToString("yyyy-MM-dd"),
        //    //                    ChangeFrequently = SitemapChangeFrequently.Monthly,
        //    //                    Priority = 0.5M
        //    //                });
        //    //            }
        //    //            DNA.Utility.XmlSerializerUtility.SerializeToXmlFile(archiveFile, typeof(SitemapUrlset), _arcivheUrlset);
        //    //        }

        //    //        _archiveSitemap.Location = rootPath + "/webshared/home/sitemaps/" + archiveFileName;
        //    //        _sitemapIndex.SiteMapFiles.Add(_archiveSitemap);
        //    //    }

        //    //    return Content(DNA.Utility.XmlSerializerUtility.SerializeToXml(typeof(SitemapIndex), _sitemapIndex), "text/xml");
        //    //}

        //    throw new NotImplementedException();
        //}

        //[OutputCache(Duration = 36000, VaryByParam = "none")]
        //public ActionResult RssFeed()
        //{
        //    //var doc = this.CurrentWeb().GetRssFeed();
        //    //string xml = DNA.Utility.XmlSerializerUtility.SerializeToXml(typeof(DNA.OpenAPI.Rss.RssDocument), doc);
        //    ////xml = xml.Replace("<?xml version=\"1.0\" encoding=\"utf-8\"?>", "<rss version=\"2.0\">");
        //    //return Content(xml, "text/xml");
        //    var rssGenerator = new RssDocumentGenerator(DependencyResolver.Current.GetServices<RssItemsGeneratorBase>());
        //    return Content(rssGenerator.GetXml(),"text/xml");
        //}

        //[OutputCache(Duration = 36000, VaryByParam = "none")]
        //public ActionResult AtomFeed()
        //{
        //    //var feed = this.CurrentWeb().GetAtomFeed();
        //    //string xml = DNA.Utility.XmlSerializerUtility.SerializeToXml(typeof(DNA.OpenAPI.Atom.AtomFeed), feed);
        //    //return Content(xml, "text/xml");
        //    var rssGenerator = new AtomFeedGenerator(DependencyResolver.Current.GetServices<AtomEntriesGeneratorBase>());
        //    //rssGenerator.SetBaseInfo(
        //    return Content(rssGenerator.GetXml(), "text/xml");
        //}
    }
}
